#pragma once
#include "gtest/gtest.h"
#include "Utility/Algebra/Matrix.h"
#include "DBManager/DBServiceFactory.h"
#include "DBManager/IService/Input/Subcase/ISubcaseService.h"
#include "DBManager/IService/Input/Coordinate/ICORD2RService.h"
#include "SGFem/Pre/Assembly/Assembly.h"
#include "SGFem/Pre/Assembly/CoordinateProcessor.h"
#include "../../Factory/ElementCalculatorFactory.h"
#include "../Conm1ElementCalculator.h"

using SG::Algebra::Matrixd;
using namespace SG::DataStructure::FEM;
using namespace SG::DBManager;
using SG::FEMSolver::AssemblyEigen;
using SG::FEMSolver::CoordinateProcessor;
using namespace SG::FEMSolver::Element;

// ElementCalculatorFactory 是否正确创建 Conm1 单元计算类
TEST (ElementCalculatorFactoryTest, CreateConm1Calculator)
{
    ElementCalculatorFactory::Register (ElementTypes::ELETYPE_Conm1, Conm1ElementCalculator::Create);

    // Test data
    const auto calculatorPtr = ElementCalculatorFactory::Create (ElementTypes::ELETYPE_Conm1);
    const auto conm1CalculatorPtr = dynamic_cast<Conm1ElementCalculator*> (calculatorPtr.get ());

    // Check
    ASSERT_TRUE (nullptr != conm1CalculatorPtr);
}

extern std::shared_ptr<DBServiceFactory> DB_SERVICEFACTORY;
class Conm1ElementCalculatorTest : public ::testing::Test
{
protected:
    static void SetUpTestSuite ()
    {
        DB_SERVICEFACTORY->ClearDatabase ();

        // 直角坐标系 100
        CORD2RData rectangularCoordSys{100, 0, {1.0, 2.0, 3.0}, {2.0, 4.0, 1.0}, {5.0, 1.0, 1.0}};
        DB_SERVICEFACTORY->get<ICORD2RService> ()->Add (rectangularCoordSys);

        // 直角坐标系 101
        rectangularCoordSys = {101, 100, {4.0, 3.0, 7.0}, {2.0, 6.0, 1.0}, {5.0, 1.0, 4.0}};
        DB_SERVICEFACTORY->get<ICORD2RService> ()->Add (rectangularCoordSys);

        // 生成节点排序
        auto& assembly = AssemblyEigen::GetInstance ();
        assembly.Clear ();
        Subcase subcase;
        assembly.Initialize (DB_SERVICEFACTORY, subcase, {1});

        // 计算坐标系原点和基矢量
        auto& coordProcessor = CoordinateProcessor::GetInstance ();
        coordProcessor.Clear ();
        coordProcessor.Initialize (DB_SERVICEFACTORY);
    }

    void SetUp () override
    {
        node = {2, 0, {15.0, 10.0, 5.0}, 0};

        element.m_id    = 2;
        element.m_g[0]  = 2;
        element.m_cId   = 0;
        element.m_m1[0] = 100.0;
        element.m_m2[0] = 10.0; element.m_m2[1] = 100.0;
        element.m_m3[0] = 10.0; element.m_m3[1] = 20.0; element.m_m3[2] = 100.0;
        element.m_m4[0] = 10.0; element.m_m4[1] = 20.0; element.m_m4[2] = 30.0; element.m_m4[3] = 200.0;
        element.m_m5[0] = 10.0; element.m_m5[1] = 20.0; element.m_m5[2] = 30.0; element.m_m5[3] = 40.0; element.m_m5[4] = 300.0;
        element.m_m6[0] = 10.0; element.m_m6[1] = 20.0; element.m_m6[2] = 30.0; element.m_m6[3] = 40.0; element.m_m6[4] = 50.0; element.m_m6[5] = 400.0;
    }

    NodeData node;
    CONM1Data element;
};

TEST_F (Conm1ElementCalculatorTest, ComputeLocalMass)
{
    // Test data
    Matrixd massTest (6, 6);
    Conm1ElementCalculator::ComputeMass (element, node, _OUT massTest);

    // Target data
    const Matrixd massTarget (6, 6, {
    100.0, 10.0,  10.0,  10.0,  10.0,  10.0,
    10.0,  100.0, 20.0,  20.0,  20.0,  20.0,
    10.0,  20.0,  100.0, 30.0,  30.0,  30.0,
    10.0,  20.0,  30.0,  200.0, 40.0,  40.0,
    10.0,  20.0,  30.0,  40.0,  300.0, 50.0,
    10.0,  20.0,  30.0,  40.0,  50.0,  400.0
    });

    // Check
    ASSERT_TRUE (massTarget == massTest);
}

TEST_F (Conm1ElementCalculatorTest, ComputeGlobalMassWithCID)
{
    // Input data
    element.m_cId = 100;

    // Test data
    Matrixd massTest (6, 6);
    Conm1ElementCalculator::ComputeMass (element, node, _OUT massTest);

    // Target data
    const Matrixd massTarget (6, 6, {
     9.10789266E+01, -7.37460323E+00, -9.40978366E+00,  5.50529779E+00, -3.22180012E+00, -1.30437950E+01,
    -7.37460323E+00,  8.50094100E+01,  4.36299273E+00,  3.04676654E+00, -1.78302304E+00, -7.21875539E+00,
    -9.40978366E+00,  4.36299273E+00,  1.23911663E+02, -2.37527221E+01,  1.39005238E+01,  5.62777252E+01,
     5.50529779E+00,  3.04676654E+00, -2.37527221E+01,  2.19768766E+02,  5.30275808E+01, -4.92148177E+01,
    -3.22180012E+00, -1.78302304E+00,  1.39005238E+01,  5.30275808E+01,  2.71858791E+02, -3.12920878E+01,
    -1.30437950E+01, -7.21875539E+00,  5.62777252E+01, -4.92148177E+01, -3.12920878E+01,  4.08372444E+02
    });

    // Check
    ASSERT_TRUE (massTarget == massTest);
}

TEST_F (Conm1ElementCalculatorTest, ComputeGlobalMassWithCPCDCID)
{
    // Input data
    node.m_defCoord  = 100;
    node.m_dispCoord = 101;
    element.m_cId    = 100;

    // Test data
    Matrixd massTest (6, 6);
    Conm1ElementCalculator::ComputeMass (element, node, _OUT massTest);

    // Target data
    const Matrixd massTarget (6, 6, {
    1.05180052E+02,  1.36226231E+01,  1.04285712E+01,  2.27791321E+01,  3.49488055E+01,  1.88843974E+01,
    1.36226231E+01,  1.07064846E+02,  1.38682293E+01,  1.49488055E+01,  2.29351536E+01,  1.23928858E+01,
    1.04285712E+01,  1.38682293E+01,  8.77551020E+01,  1.72320126E+01,  2.64381563E+01,  1.42857143E+01,
    2.27791321E+01,  1.49488055E+01,  1.72320126E+01,  3.07229923E+02,  8.09946368E+01,  8.45329698E+01,
    3.49488055E+01,  2.29351536E+01,  2.64381563E+01,  8.09946368E+01,  2.54402730E+02,  1.38682293E+01,
    1.88843974E+01,  1.23928858E+01,  1.42857143E+01,  8.45329698E+01,  1.38682293E+01,  3.38367347E+02
    });

    // Check
    ASSERT_TRUE (massTarget == massTest);
}